home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / readers / skim-0.8 / skim-0 / skim-0.8.4 / NNTPStream.c < prev    next >
C/C++ Source or Header  |  1996-02-18  |  8KB  |  310 lines

  1. /*
  2.  * NAME
  3.  *   NNTPStream.c
  4.  * COPYRIGHT
  5.  *   Skim - Off-line news reading package optimized for slow lines.
  6.  *   Copyright (C) 1996  Rene W.J. Pijlman
  7.  *
  8.  *   This program is free software; you can redistribute it and/or modify
  9.  *   it under the terms of the GNU General Public License as published by
  10.  *   the Free Software Foundation; either version 2 of the License, or
  11.  *   (at your option) any later version.
  12.  * 
  13.  *   This program is distributed in the hope that it will be useful,
  14.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *   GNU General Public License for more details.
  17.  *
  18.  *   You should have received a copy of the GNU General Public License
  19.  *   along with this program; if not, write to the Free Software
  20.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  * VERSION
  22.  *   Skim version 0.8.4.
  23.  */
  24.  
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <sys/types.h>
  28. #include <sys/socket.h>
  29. #include <netinet/in.h>
  30. #include <netdb.h>
  31. #include <unistd.h>
  32. #include <assert.h>
  33.  
  34. #include "VarBuf.h"
  35. #include "Skim.h"
  36. #include "NNTPStream.h"
  37. #include "StandardIO.h"
  38.  
  39.  
  40. FILE_ID("$Header: /home/rene/sys/CVS_MasterSourceRepository/skim/NNTPStream.c,v 1.5 1996/02/18 11:40:19 rene Exp $");
  41.  
  42. /*
  43.  * Return a socket with a TCP/IP connection to the specified service (port)
  44.  * on the specified host.
  45.  */
  46. static int TCP_ConnectionOpen( char * HostName, char * ServiceName )
  47. {
  48.     int Status;
  49.     struct protoent * Protocol;
  50.     struct hostent * Host;
  51.     struct servent * Service;
  52.     struct sockaddr_in SocketAddress;
  53.     int Socket;
  54.  
  55.     Protocol = getprotobyname( "tcp" );
  56.     if ( Protocol != NULL )
  57.     {
  58.     Host = gethostbyname( HostName );
  59.     if ( Host != NULL )
  60.     {
  61.         Service = getservbyname( (char *)ServiceName, "tcp" );
  62.         if ( Service != NULL )
  63.         {
  64.         Socket = socket( AF_INET, SOCK_STREAM, Protocol->p_proto );
  65.         if ( Socket != NotASocket )
  66.         {
  67.             memset( &SocketAddress, 0, sizeof SocketAddress );
  68.             SocketAddress.sin_family = AF_INET;
  69.             memmove( (char *)&SocketAddress.sin_addr,
  70.                  Host->h_addr, Host->h_length );
  71.             SocketAddress.sin_port = Service->s_port;
  72.  
  73.             Status = connect( Socket,
  74.                       (struct sockaddr *)&SocketAddress,
  75.                       sizeof( SocketAddress ) );
  76.             if ( Status == -1 )
  77.             {
  78.                 SIOPrintError( "connect" );
  79.             SIOPrintf( StandardError,
  80.     "Does the NNTPSERVER environment variable point to your news server?\n"
  81.     "Note that not all servers allow access to all clients.\n" );
  82.                 exit( EXIT_FAILURE );
  83.             }
  84.         }
  85.         else
  86.         {
  87.             SIOPrintError( "socket" );
  88.             exit( EXIT_FAILURE );
  89.         }
  90.         }
  91.         else
  92.         {
  93.         SIOPrintf( StandardError,
  94.              "Cannot translate service name '%s' to port number\n"
  95.              "Check /etc/protocols.\n",
  96.              ServiceName );
  97.             exit( EXIT_FAILURE );
  98.         }
  99.     }
  100.     else
  101.     {
  102.         herror( "gethostbyname" );
  103.         SIOPrintf( StandardError,
  104.     "Does the NNTPSERVER environment variable point to your news server?\n" );
  105.         exit( EXIT_FAILURE );
  106.     }
  107.     }
  108.     else
  109.     {
  110.        SIOPrintf( StandardError,
  111.               "Cannot translate protocol name 'tcp' to number\n" 
  112.               "Check /etc/protocols.\n" );
  113.        exit( EXIT_FAILURE );
  114.     }
  115.  
  116.     return Socket;
  117. }
  118.  
  119.  
  120. /* Return a bidirectional standard I/O stream to the NNTP news server. */
  121. StandardIO NNTPStreamOpen( void )
  122. {
  123.     int Socket;
  124.     char * NNTP_Server;
  125.     StandardIO NewsServer = SIOCreate();
  126.     VarBuf PseudoFileName = VBCreate();
  127.     char * p;
  128.     Boolean TalkingToINN = False;
  129.  
  130.     if ( ( NNTP_Server = getenv( "NNTPSERVER" ) ) == NULL )
  131.     {
  132.         SIOPrintf( StandardError,
  133.                    "Environment variable NNTPSERVER is not set\n" );
  134.         exit( EXIT_FAILURE );
  135.     }
  136.  
  137.     /*
  138.      * The user can enable an INN-specific kludge by appending ":INN" to
  139.      * the hostname in NNTPSERVER.
  140.      */
  141.     if ( ( p = strchr( NNTP_Server, ':' ) ) != NULL )
  142.     {
  143.         if ( !strcasecmp( p, ":INN" ) )
  144.         {
  145.             *p = '\0';
  146.             TalkingToINN = True;
  147.         }
  148.     }
  149.  
  150.     Socket = TCP_ConnectionOpen( NNTP_Server, "nntp" );
  151.  
  152.     VBPrintf( PseudoFileName, "<NNTP connection to %s>", NNTP_Server );
  153.     SIOFileOpenFileDescriptorVB( NewsServer, Socket, PseudoFileName,
  154.                                  OpenModeReadAndWriteDiscardOld );
  155.  
  156.     CheckStatusResponse( NewsServer, NULL, "2", NULL, TERMINATE_ON_ERROR );
  157.  
  158.     /*
  159.      * INN has a design bug: when a client connects to the NNTP port INN
  160.      * may or may not speak NNTP depending on various circumstances. 
  161.      *
  162.      * On Linux, UNIX domain sockets don't work. We have to set the INN
  163.      * configuration option HAVE_UNIX_DOMAIN to DONT, therefore, and the local
  164.      * hostname to the hosts.nntp config file. This causes INN to not speak
  165.      * NNTP when the client is on the same IP-address as the server, unless the
  166.      * reader issues the non-NNTP command "mode reader". This is explained in
  167.      * the INN FAQ.
  168.      *
  169.      * This is a kludge of course, but I needed it to be able to test skim
  170.      * with INN on my local linux machine.
  171.      */
  172.     if ( TalkingToINN )
  173.     {
  174.         SIOInternetCommand( NewsServer, "mode reader" );
  175.         CheckStatusResponse( NewsServer, NULL, "2", NULL, TERMINATE_ON_ERROR );
  176.     }
  177.  
  178.     VBDestroy( PseudoFileName );
  179.  
  180.     return NewsServer;
  181. }
  182.  
  183.  
  184. void NNTPStreamClose( StandardIO NewsServer )
  185. {
  186.     if ( NewsServer != NULL && SIOIsOpenForWrite(NewsServer) )
  187.     {
  188.     SIOInternetCommand( NewsServer, "quit" );
  189.     SIOFileClose( NewsServer );
  190.     }
  191.  
  192.     SIODestroy( NewsServer );
  193. }
  194.  
  195.  
  196. Boolean CheckStatusResponse(
  197.     StandardIO NewsServer,
  198.     const char * Group,
  199.     const char * MustStartWithStatusResponse,
  200.     const char * ExtraMessage,  /* May be NULL. */
  201.     Boolean TerminateOnError )
  202. {
  203.     Boolean OK = True;
  204.     VarBuf StatusResponse = VBCreate();
  205.  
  206.     SIOFlushBuffers( NewsServer );
  207.  
  208.     VBReadLine( StatusResponse, NewsServer, WITHOUT_NEWLINE );
  209.  
  210.     if ( strncmp( VBAsString(StatusResponse), MustStartWithStatusResponse,
  211.                   strlen(MustStartWithStatusResponse) ) )
  212.     {
  213.     OK = False;
  214.  
  215.     if ( Group != NULL )
  216.     {
  217.         SIOPrintf( StandardError, "On group %s:\n", Group );
  218.     }
  219.  
  220.     SIOPrintf( StandardError,
  221.                        "Error from NNTP server:\n%V",
  222.                        StatusResponse );
  223.  
  224.         if ( ExtraMessage != NULL )
  225.         {
  226.             SIOPrintf( StandardError, "\n%s\n", ExtraMessage );
  227.         }
  228.  
  229.     if ( TerminateOnError )
  230.     {
  231.         exit( EXIT_FAILURE );
  232.     }
  233.     }
  234.  
  235.     VBDestroy( StatusResponse );
  236.  
  237.     return OK;
  238. }
  239.  
  240.  
  241. /*
  242.  * Read a text response from the NNTP server, convert it from RFC-977 format
  243.  * to UNIX text file format, and write it to `ResponseBuffer' (if not NULL),
  244.  * and/or `ResponseFile' (if not NULL), and or pass it as a parameter to 
  245.  * function Output (if not NULL). The conversions which are applied are:
  246.  *
  247.  *   - CR-LF pairs are converted to LF.
  248.  *   - Double periods on the first character of a line are collapsed to a 
  249.  *     single period.
  250.  *   - The terminating line with only a period is removed.
  251.  *   - The function Convert is called for every line, if Convert is not NULL.
  252.  */
  253. void GetTextResponse(
  254.     StandardIO NewsServer,
  255.     ConversionFunction Convert,
  256.     VarBuf ResponseBuffer,
  257.     StandardIO Response,
  258.     OutputFunction Output )
  259. {
  260.     Boolean Finished = False;
  261.     VarBuf LineOfTextResponse = VBCreate();
  262.  
  263.     while ( !Finished &&
  264.             VBReadLine( LineOfTextResponse, NewsServer, WITHOUT_NEWLINE ) )
  265.     {
  266.     if ( !strcmp( VBAsString(LineOfTextResponse), ".\r" ) )
  267.     {
  268.         Finished = True;
  269.     }
  270.     else if ( !strncmp( VBAsString(LineOfTextResponse), "..", 2 ) )
  271.     {
  272.         VBShiftLeft( LineOfTextResponse, 1 );
  273.     }
  274.  
  275.     if ( !Finished )
  276.     {
  277.         if ( VBSize(LineOfTextResponse) >= 1 &&
  278.          *(VBAsString(LineOfTextResponse) +
  279.            VBSize(LineOfTextResponse) - 1) == '\r' )
  280.         {
  281.         VBTruncate(LineOfTextResponse, VBSize(LineOfTextResponse) - 1);
  282.         }
  283.  
  284.         if ( Convert != NULL )
  285.         {
  286.             Convert( LineOfTextResponse );
  287.         }
  288.  
  289.         if ( Response != NULL )
  290.         {
  291.         SIOPrintf( Response, "%V\n", LineOfTextResponse );
  292.         }
  293.  
  294.         if ( ResponseBuffer != NULL )
  295.         {
  296.         VBPrintf( ResponseBuffer, "%V\n", LineOfTextResponse );
  297.         }
  298.  
  299.         if ( Output != NULL )
  300.         {
  301.             Output( LineOfTextResponse );
  302.         }
  303.     }
  304.  
  305.     VBReset( LineOfTextResponse );
  306.     }
  307.  
  308.     VBDestroy(LineOfTextResponse);
  309. }
  310.